<?php

/**
 * 包含获取数据支持方法的类.
 *
 * @author edikud
 * @package Db
 * @copyright Copyright (c) 2019 Cola (http://www.mcooo.com)
 * @license GNU General Public License 2.0
 */
class Db
{
    /**
     * 前缀
     *
     * @access private
     * @var string
     */
    private $_prefix = null;
	
    /**
     * 数据库适配器
     * @var Db_Adapter
     */
    private $_adapter;
	
    /**
     * 适配器名称
     *
     * @access private
     * @var string
     */
    private $_adapterName;

    /**
     * 实例化的数据库对象
     * @var Db
     */
    private static $_instance;
	
    /** 读取数据库 */
    const READ = 1;

    /** 写入数据库 */
    const WRITE = 2;
	
    /**
     * 默认配置
     *
     * @auth private
     * @var Config
     */
    private $_config = array();

    /**
     * 连接池
     *
     * @auth private
     * @var array
     */
    private $_pool = array();

    /**
     * 已经连接
     *
     * @auth private
     * @var array
     */
    private $_connectedPool = array();
	
    /**
     * Debug or not
     *
     * @var boolean
     */
    public static $debug = false;

    /**
     * Log
     *
     * @var array
     */
    public static $log = array();
	
    /**
     * 数据库类构造函数
     *
     * @param mixed $adapterName 适配器名称
     * @param string $prefix 前缀
     * @throws Db_Exception
     */
    public function __construct($adapterName, $prefix = 'cola_')
    {
        /** 获取适配器名称 */
        $this->_adapterName = $adapterName;

        /** 数据库适配器 */
        $adapterName = 'Db_' . $adapterName;

        if (!call_user_func(array($adapterName, 'isAvailable'))) {
            throw new Db_Exception("Adapter {$adapterName} is not available");
        }

        $this->_prefix = $prefix;

        /** 初始化内部变量 */
        $this->_pool = array();
        $this->_connectedPool = array();
        $this->_config = array();

        //实例化适配器对象
        $this->_adapter = new $adapterName($prefix);
    }
	
    /**
     * 获取适配器名称
     *
     * @access public
     * @return string
     */
    public function getAdapterName()
    {
        return $this->_adapterName;
    }
	
    public function getAdapter()
    {
        return $this->_adapter;
    }
    /**
     * 获取表前缀
     *
     * @access public
     * @return string
     */
    public function getPrefix()
    {
        return $this->_prefix;
    }

    /**
     * getConfig  
     * 
     * @access public
     * @return array
     */
    public function getConfig()
    {
        return $this->_config;
    }

    /**
     * 重置连接池
     * 
     * @return void
     */
    public function flushPool()
    {
        $this->_connectedPool = array();
    }

    /**
     * 选择数据库
     * 
     * @param int $op 
     * @return Db_Adapter
     * @throws Db_Exception
     */
    public function selectDb($op)
    {
        if (!isset($this->_connectedPool[$op])) {
            if (empty($this->_pool[$op])) {
                /** Db_Exception */
                throw new Db_Exception('Missing Database Connection');
            }
            
            //获取相应读或写服务器连接池中的一个
            $selectConnection = rand(0, count($this->_pool[$op]) - 1); 
            //获取随机获得的连接池配置
            $selectConnectionConfig = $this->_config[$this->_pool[$op][$selectConnection]];
            $selectConnectionHandle = $this->_adapter->connect($selectConnectionConfig);
            $this->_connectedPool[$op] = &$selectConnectionHandle;
        }

        return $this->_connectedPool[$op];
    }
	
    /**
     * 为多数据库提供支持
     *
     * @access public
     * @param Db $db 数据库实例
     * @param integer $op 数据库操作
     * @return void
     */
    public function addServer($config, $op)
    {
        $this->_config[] = Container::factory($config);
        $key = count($this->_config) - 1;

        /** 将连接放入池中 */
        switch ($op) {
            case Db::READ:
            case Db::WRITE:
                $this->_pool[$op][] = $key;
                break;
            default:
                $this->_pool[Db::READ][] = $key;
                $this->_pool[Db::WRITE][] = $key;
                break;
        }
    }

    /**
     * 清空数据表
     * 
     * @param int $op 
     * @return string
     */
    public function truncate()
    {
        return $this->_adapter->truncate($this->selectDb(Db::WRITE));
    }
	
    /**
     * 获取版本
     * 
     * @param int $op 
     * @return string
     */
    public function version($op = Db::READ)
    {
        return $this->_adapter->version($this->selectDb($op));
    }
	
    /**
     * 设置默认数据库对象
     *
     * @access public
     * @param Db $db 数据库对象
     * @return void
     */
    public static function setInstance(Db $db)
    {
        self::$_instance = $db;
    }

    /**
     * 获取数据库实例化对象
     * 用静态变量存储实例化的数据库对象,可以保证数据连接仅进行一次
     *
     * @return Db
     * @throws Db_Exception
     */
    public static function instance()
    {
        if (empty(self::$_instance)) {
            /** Db_Exception */
            throw new Db_Exception('Missing Database Object');
        }

        return self::$_instance;
    }

    /**
     * 获取SQL词法构建器实例化对象
     *
     * @return Db_Query
     */
    public function sql()
    {
        return new Db_Query($this);
    }
	
    /**
     * 执行查询语句
     *
     * @param string $query <code>select * from user where id = ?</code>
     * @param array $params <code>参数的值 array(1)</code>
     * @return mixed
     */
    public function query()
    {
        $args = func_get_args();
        return call_user_func_array(array($this->sql(), 'sql'), $args);
    }
	
    /**
     * 选择查询字段
     *
     * @access public
     * @param mixed $field 查询字段
     * @return Db_Query
     */
    public function select()
    {
        $args = func_get_args();
        return call_user_func_array(array($this->sql(), 'select'), $args ? $args : array('*'));
    }

    /**
     * 更新记录操作(UPDATE)
     *
     * @param string $table 需要更新记录的表
     * @return Db_Query
     */
    public function update($table)
    {
        return $this->sql()->update($table);
    }

    /**
     * 删除记录操作(DELETE)
     *
     * @param string $table 需要删除记录的表
     * @return Db_Query
     */
    public function delete($table)
    {
        return $this->sql()->delete($table);
    }

    /**
     * 插入记录操作(INSERT)
     *
     * @param string $table 需要插入记录的表
     * @return Db_Query
     */
    public function insert($table)
    {
        return $this->sql()->insert($table);
    }

    /**
     * 检查连接是否可用
     * @param  Link $dbconn 数据库连接
     * @return Boolean
     */
    public function isAvailable()
    {
        return $this->_adapter->isAvailable();
    }
	
    /**
     * SQL语句调试.
     * debug===true时, 默认记录在sql.log
     *
     * @param string $sql <code>select * from table where id = ?</code>
     * @param float $execute_time 当前sql执行时间
     *
     * @return void
     */
    public static function debug($sql, $params = array(), $execute_time = 0)
    {
        if (self::$debug) {
            $sql = preg_replace_callback('/[?]/', function ($k) use($params) {
                static $i = 0;
                return sprintf("'%s'", $params[$i++]);
            }, $sql);
            if (count(self::$log) < 1000) {
                self::$log[] = ' <font color="red">[time:' . number_format($execute_time, 4) . 's]</font> ' . $sql;
            }
        }
    }
}